# -*- coding: utf-8 -*-
from django.db import models
from hashlib import md5
from django.utils import six, timezone
from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
from django.contrib.auth.validators import UnicodeUsernameValidator
from django.utils.translation import ugettext_lazy as _
from .base import BaseModel, CreatedAndUpdatedModel
from django.contrib.auth.hashers import (
    check_password, make_password,
)


class Permission(CreatedAndUpdatedModel):
    parent_id = models.IntegerField('父节点id', null=True, default=None)
    name = models.CharField('资源名称', max_length=64, unique=False, default='', blank=True)
    description = models.CharField('资源描述', max_length=512, unique=False, blank=True)
    url = models.CharField('地址', max_length=512, unique=False, blank=True)
    permission_code = models.CharField('菜单编码', max_length=50, unique=False, blank=True)
    icon = models.CharField('菜单图标', max_length=255, unique=False, blank=True)
    cate = models.IntegerField('类型:0:菜单,1:按钮', null=False, default=0)
    show_order = models.IntegerField('排序号', null=False, default=1)
    disabled = models.IntegerField('是否启用', null=False, default=0, db_index=True)

    @property
    def sub_cates(self):
        if not hasattr(self, '_sub_cates'):
            self._sub_cates = self._get_sub_cates()
        return self._sub_cates

    def _get_sub_cates(self):
        objs = self.__class__.objects.filter(disabled=0).filter(parent_id=self.id)
        return objs.all()

    def cate_level_1_to_dict(self):
        relts = []
        relts.append(self.to_dict(level=1))
        for l2 in self.sub_cates:
            relts.append(l2.to_dict(level=2))
            for l3 in l2.sub_cates:
                relts.append(l3.to_dict(level=3))
        return relts

    @property
    def cate_desc(self):
        return '菜单' if self.cate == 0 else '按钮'

    @property
    def disabled_desc(self):
        return self.get_disabled_desc(self.disabled)

    def to_tree_dict(self):
        return dict(
            value = self.id,
            label = self.name,
            children = []
        )

    def to_dict(self, m2m_fields=[], o2o_fields=[], level=1):
        return {
            'id': self.id,
            'name': self.name,
            'description': self.description,
            'cate': self.cate,
            'cate_desc': self.cate_desc,
            'url': self.url,
            'icon': self.icon,
            'parent_id': self.parent_id,
            'permission_code': self.permission_code,
            'show_order': self.show_order,
            'disabled': self.disabled,
            'disabled_desc': self.disabled_desc,
            'created_at': self.created_at,
            'level': level,
        }

    class Meta:
        db_table = 'bms_permission'


class Group(CreatedAndUpdatedModel):
    name = models.CharField('角色名', max_length=64, unique=True, blank=True)
    description = models.CharField('角色描述', max_length=512, unique=False, blank=True)
    show_order = models.IntegerField('排序号', null=False, default=1)
    disabled = models.IntegerField('是否启用', null=False, default=0, db_index=True)
    permissions = models.ManyToManyField(Permission, through='GroupPermission', through_fields=('group', 'permission'))

    @property
    def disabled_desc(self):
        return self.get_disabled_desc(self.disabled)

    def to_dict(self, m2m_fields=[], o2o_fields=[]):
        d = {
            'id': self.id,
            'name': self.name,
            'description': self.description,
            'show_order': self.show_order,
            'disabled': self.disabled,
            'disabled_desc': self.disabled_desc,
            'created_at': self.created_at
        }
        for field in m2m_fields:
            fields = getattr(self, field, [])
            if fields:
                d[field] = [f.to_dict() for f in fields.all()]
        return d

    class Meta:
        db_table = 'bms_group'


class GroupPermission(models.Model):
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    permission = models.ForeignKey(Permission, on_delete=models.CASCADE)

    class Meta:
        db_table = 'bms_group_permissions'


# Create your models here.
class BMSUserManager(BaseUserManager):

    def _create_user(self, username, email, password, **extra_fields):
        """
        Creates and saves a User with the given username, email and password.
        """
        if not username:
            raise ValueError('The given username must be set')
        email = self.normalize_email(email)
        username = self.model.normalize_username(username)
        user = self.model(username=username, email=email, **extra_fields)
        user.set_password(password)
        user.full_clean()
        user.save(using=self._db)
        return user

    def create_user(self, username, email=None, password=None, **extra_fields):
        extra_fields.setdefault('is_staff', False)
        extra_fields.setdefault('is_superuser', False)
        return self._create_user(username, email, password, **extra_fields)

    def create_superuser(self, username, email, password, **extra_fields):
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)

        if extra_fields.get('is_staff') is not True:
            raise ValueError('Superuser must have is_staff=True.')
        if extra_fields.get('is_superuser') is not True:
            raise ValueError('Superuser must have is_superuser=True.')

        return self._create_user(username, email, password, **extra_fields)


class BMSUser(AbstractBaseUser):
    username_validator = UnicodeUsernameValidator()

    username = models.CharField(
        _('username'),
        max_length=150,
        unique=True,
        help_text=_('Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.'),
        validators=[username_validator],
        error_messages={
            'unique': _("A user with that username already exists."),
        },
    )
    first_name = models.CharField(_('first name'), max_length=30, blank=True)
    last_name = models.CharField(_('last name'), max_length=30, blank=True)
    email = models.EmailField(_('email address'), blank=True)
    is_staff = models.BooleanField(
        _('staff status'),
        default=False,
        help_text=_('Designates whether the user can log into this admin site.'),
    )
    is_active = models.BooleanField(
        _('active'),
        default=True,
        help_text=_(
            'Designates whether this user should be treated as active. '
            'Unselect this instead of deleting accounts.'
        ),
    )
    date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
    is_superuser = models.BooleanField(
        _('superuser status'),
        default=False,
        help_text=_(
            'Designates that this user has all permissions without '
            'explicitly assigning them.'
        ),
    )

    groups = models.ManyToManyField(Group, through='UserGroup', through_fields=('user', 'group'))

    objects = BMSUserManager()

    EMAIL_FIELD = 'email'
    USERNAME_FIELD = 'username'
    DEFAULT_PASSWORD = '12345678'

    def reset_password(self):
        self.password = make_password(BMSUser.DEFAULT_PASSWORD)
        self.save()
        return self

    def to_option_dict(self):
        return dict(
                id=self.id,
                label=self.username
            )

    @property
    def superuser(self):
        return self.is_superuser

    def _build_token(self):
        if not hasattr(self, '_token'):
            s = '%s|%s|huanxicm' % (self.id, self.username)
            self._token = md5(s.encode('utf8')).hexdigest() + '|%s' % self.id
        return self._token
    
    @property
    def groups_info(self):
        group_name_list = [ g.name for g in self.get_groups() ]
        return ','.join(group_name_list)

    @property
    def groups_ids(self):
        return [ g.id for g in self.get_groups() ]
    
    def get_groups(self):
        if not hasattr(self, '_groups_data'):
            self._groups_data = self.groups.all()
        return self._groups_data

    @property
    def token(self):
        return self._build_token()
    
    @property
    def roles(self):
        return [ g.id for g in self.groups.all() ]

    @property
    def is_active_desc(self):
        return '启用' if self.is_active == 1 else '禁用'

    def to_login_dict(self):
        return {
            'token': self.token,
        }

    def get_user_permissions(self):
        ug = UserGroup.objects.filter(user=self).first()
        return [ p.permission_code for p in ug.group.permissions.all()]

    def to_user_info_dict(self):
        return {
            'id': self.id,
            'username': self.username,
            'nickname': self.first_name,
            'roles': self.roles,
            'user_permissions': self.get_user_permissions(),
            'avatar': 'http://img3.imgtn.bdimg.com/it/u=3438414829,874981644&fm=26&gp=0.jpg'
        }

    def to_dict(self, m2m_fields=[], o2o_fields=[]):
        d = dict(
            id=self.id,
            username=self.username,
            token=self.token,
            date_joined=self.date_joined,
            last_login=self.last_login,
            full_name=self.get_full_name(),
            first_name=self.first_name,
            second_name=self.last_name,
            role_names=self.groups_info,
            role_ids=self.groups_ids,
            is_active_desc=self.is_active_desc,
        )
        for field in m2m_fields:
            fields = getattr(self, field, [])
            if fields:
                d[field] = [ f.to_dict() for f in fields.all() ]
        return d

    @property
    def is_noraml(self):
        return self.status == self.STATUS_NORMAL

    @property
    def is_disable(self):
        return self.status == self.STATUS_DISABLE

    def clean(self):
        super(BMSUser, self).clean()
        self.email = self.__class__.objects.normalize_email(self.email)

    def get_full_name(self):
        """
        Returns the first_name plus the last_name, with a space in between.
        """
        full_name = '%s %s' % (self.first_name, self.last_name)
        return full_name.strip()

    def get_short_name(self):
        "Returns the short name for the user."
        return self.first_name

    class Meta:
        db_table = 'bms_user'


class UserGroup(models.Model):
    user = models.ForeignKey(BMSUser, on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE)

    class Meta:
        db_table = 'bms_user_groups'
